package com.jiyongjun.bms.tool;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * description:对象工具类
 *
 * @author yongjun.ji
 * @date 2018/6/11 11:45
 */
public class BeanUtil {
    private static final Logger logger = LoggerFactory.getLogger(BeanUtil.class);

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    /**
     * 比较两个对象的差异，返回map<字段名,变更后的值>
     *
     * @param originObj
     * @param nowObj
     * @return
     */
    public static <T> Map<String, Object> diff(T originObj, T nowObj) {
        if (!originObj.getClass().isAssignableFrom(nowObj.getClass())) {
            throw new ClassCastException("originObj and nowObj are not same class type");
        }
        // 有差异的map<字段名，变更后的值>
        Map<String, Object> modifyMap = new HashMap<String, Object>();
        if (null == originObj || null == nowObj) {
            if (null == originObj && null == nowObj) {
                return null;
            } else if (null == nowObj) {
                return modifyMap;
            } else {
                return OBJECT_MAPPER.convertValue(nowObj, new TypeReference<Map<String, Object>>() {
                });
            }
        }

        Map<String, Object> originObjMap = OBJECT_MAPPER.convertValue(originObj, new TypeReference<Map<String, Object>>() {
        });
        Map<String, Object> nowObjMap = OBJECT_MAPPER.convertValue(nowObj, new TypeReference<Map<String, Object>>() {
        });
        for (String field : nowObjMap.keySet()) {
            Object originValue = originObjMap.get(field);
            Object nowValue = nowObjMap.get(field);
            // 目前最多只比较两级
            if (nowValue instanceof LinkedHashMap) {
                LinkedHashMap<String, Object> originSubMap = (LinkedHashMap<String, Object>) originValue;
                LinkedHashMap<String, Object> nowSubMap = (LinkedHashMap<String, Object>) nowValue;
                for (String key : nowSubMap.keySet()) {
                    if (isNotEquals(originSubMap.get(key), nowSubMap.get(key))) {
                        modifyMap.put(field + "." + key, nowSubMap.get(key));
                    }
                }
            } else {
                if (isNotEquals(originValue, nowValue)) {
                    modifyMap.put(field, nowValue);
                }
            }
        }
        return modifyMap;
    }

    /**
     * 对象某一个字段的值是否改变了
     *
     * @param origin
     * @param now
     * @param fieldName
     * @return
     */
    public static <T> boolean isChanged(T origin, T now, String fieldName) {
        Object oldVal = null;
        Object newVal = null;
        try {
            if (origin != null) {
                oldVal = BeanUtils.getProperty(origin, fieldName);
            }
            if (now != null) {
                newVal = BeanUtils.getProperty(now, fieldName);
            }
        } catch (Exception e) {
            logger.error("获取字段" + fieldName + "的值异常", e);
        }
        if (oldVal == null) {
            return newVal != null;
        } else {
            return !oldVal.equals(newVal);
        }
    }

    /**
     * 两个对象是否相等
     * <p>依赖对象的equals方法</p>
     *
     * @param t
     * @param t2
     * @param <T>
     * @return
     */
    public static <T> boolean isEquals(T t, T t2) {
        if (t == null) {
            return t2 == null;
        } else {
            return t.equals(t2);
        }
    }

    /**
     * 两个对象是否不相等
     * <p>依赖对象的equals方法</p>
     *
     * @param t
     * @param t2
     * @param <T>
     * @return
     */
    public static <T> boolean isNotEquals(T t, T t2) {
        return !isEquals(t, t2);
    }

    /**
     * 比较两个对象是否相等
     * <p>不依赖对象的equals方法</p>
     *
     * @param source source object
     * @param target target object
     * @return true-equal
     */
    public static boolean isEuqals(Object source, Object target) {
        if (null == source || null == target) {
            return false;
        }
        if (!source.getClass().isAssignableFrom(target.getClass())) {
            return false;
        }
        Map<String, Object> sourceMap = OBJECT_MAPPER.convertValue(source, new TypeReference<Map<String, Object>>() {
        });
        Map<String, Object> targetMap = OBJECT_MAPPER.convertValue(target, new TypeReference<Map<String, Object>>() {
        });
        return isEquals(sourceMap, targetMap);
    }

    /**
     * 比较两个对象是否相等
     * <p>不依赖对象的equals方法</p>
     *
     * @param source             source object
     * @param target             target object
     * @param comparedProperties only compare fields in this map
     * @return rue-equal
     */
    public static boolean isEuqals(Object source, Object target, Map<String, String> comparedProperties) {
        if (null == source || null == target) {
            return null == source && null == target;
        }
        if (!source.getClass().isAssignableFrom(target.getClass())) {
            return false;
        }
        Map<String, Object> sourceMap = OBJECT_MAPPER.convertValue(source, new TypeReference<Map<String, Object>>() {
        });
        Map<String, Object> targetMap = OBJECT_MAPPER.convertValue(target, new TypeReference<Map<String, Object>>() {
        });
        for (String k : sourceMap.keySet()) {
            if (comparedProperties != null && !comparedProperties.containsKey(k)) {
                continue;
            }
            Object v = sourceMap.get(k);
            Object targetValue = targetMap.get(k);
            if (isNotEquals(v, targetValue)) {
                return false;
            }
        }
        return true;
    }
}
